CSS - tutorial - 13 - @ rules (at-rules)

revision:


Content

CSS at-rules are very practical in terms of telling CSS how to behave. general rules or regular rules nesting rules or nested rules feature queries - @supports @property @scope practical examples


CSS at-rules are very practical in terms of telling CSS how to behave.

top

The unique identifier is the @mark that comes before these rules. The at sign, '@' is followed by an identifier and includes everything up to the next semicolon ';', or the next CSS block, whichever comes first.

statement at-rules

syntax: @identifier (RULE)

these end in a semicolon. There are several statement at-rules, designated by their identifiers, each with a different syntax:

@charset : an algorithm (has the syntactic form of an at-rule, but isn't a definition) that determines the fallback character set used by the style sheet.

@import : tells the CSS engine to include an external style sheet (CSS cascade and inheritance).

@layer : defines the order of precedence in case of multiple cascade layers. Also used as a block at-rule to define a layer's styles.

@namespace : defines a default namespace for a style sheet or a namespace prefix that a selector only matches if the namespace and other selector components match.

block at-rules

syntax: @identifier (RULE)

these end in a {}-block that contain nested rules, other at-rules, or property or descriptor declarations.

@counter-style : define custom counter styles and extend predefined list styles.

@container : a conditional group rule that applies its content if the container meets the s.

@font-face : defines font resource locations, both local and external, along with the style characteristics for when those resources are used with a declared font-family.

@font-feature-values (plus @swash, @ornaments, @annotation, @stylistic, @styleset and @character-variant) controls font display per font-family by defining font-specific alternates, or custom names, to feature indexes in font-variant-alternates in OpenType.

@font-palette-values : allows you to customize the default values of a font-palette.

@keyframes (and the @-webkit-keyframes alias) : define a named animation by describing defining CSS styles for intermediate steps (or keyframes) in the animation sequence.

@layer : creates a named cascade layer with the CSS rules for that layer inside (CSS cascade and inheritance). Also used as a statement at-rule to define the order of precedence in case of multiple cascade layers.

@media: a conditional group rule that applies its content if the device meets the criteria of the condition defined using a media query.

@page : specifies aspects of a page to be printed, such as its dimensions, orientation, and margins.

@position-try : defines custom position options which can be used to define fallback positioning and alignment options for anchor-positioned elements.

@property : defines a CSS custom property, allowing for property type checking and constraining, setting default values, and defining whether a custom property can inherit values or not.

@scope : defines a scope in which to apply them to selected elements and the styles to apply to the elements in that scope.

@starting-style : define the starting property values for an element to transition from when the element receives its first style update, such as when transitioning from "display: none".

@supports : a conditional group rule applies its content if the browser supports the CSS features of the given condition.

view-transition : opts the current document into a view transition, and the destination document as well in the case of cross-document navigation transitions.

These rules also can be divided into two groups: general rules and nesting rules.


general rules or regular rules

top

General CSS at-rules need to be placed on top of the stylesheet, before all the other CSS attributes and properties, because they are defining the general settings of the CSS rules and will not be overwritten by other rules.

Syntax: @identifier (rule)

examples

@charset "utf-8";

@charset is the first CSS at-rule that needs to be declared in the stylesheet for defining character encoding and no other rules should precede it.

There are other ways of handling charset, like placing it on the HTTP header, but there are certain use cases for declaring it in CSS.
For example, when we are using non-ASCII characters for the content property, the browser has different ways of figuring out the character encoding: setting it in the calling HTML file, or the server might set content type with a certain character encoding, in the response header.

In most cases, this is managed, but in case the encoding of the calling or returned HTML is different from a certain stylesheet, then @charset needs to be declared in the CSS file.

examples

Example: @charset

          @charset "UTF-8";
          h1::before {content: "@charset ";}
          q {color: blue;}
          q::before {content: "∅  ";}
          q::after {content: "  ∅";}
      

@import is used to include CSS from another source in a current CSS file.

When parsing the CSS file and encountering a @import rule, the browser makes an HTTP request to fetch the external stylesheet and include its CSS properties right where the @import rule is declared.

Since this rule was built to help developers include stylesheets from other sources, it is not possible to include it within any of the conditional group at-rules like @media, @page, and @document.

examples

Example: @import - wrong usage

        /* iPhone in Portrait and Landscape */
        @media only screen{
          @import 'custom.css';
        }
      

However, we can specify media-dependent @import rules, to avoid fetching resources for unsupported media types.

examples

Example: @import

 
      @import 'custom.css' screen and (orientation:landscape);
      

The @namespace rule was designed to help XML based namespaces that would prevent duplicate styles from interfering with each other.

Even though there are more sophisticated concepts like "SMACSS", there could be potential use cases for this rule in CSS.

The @namespace rule helps apply scoping for CSS that mix styles from different XML namespaces. Examples of XML namespaces are HTML, SVG, MathML, XLink, etc. This way, there will be no styling collisions between elements from different namespaces.


nesting rules or nested rules

top

These are a subset of CSS at-rules that store a subset of additional statements within them, some of which might be conditional to a specific situation. They usually follow up after the general rules.

Syntax: @identifier (rule)

@document is a unique rule that allows you to specify styles for a certain page, without affecting the styles of other pages.

This feature is deprecated and no longer recommended.

This page-based style customization comes in different forms:

You can specify the rules for a specific URL:

examples

Example: @document - url

        @document url(https://example.com/)
      

You can specify the rules for pages that their URLs start with:

examples

Example: @document - url

        @document  url-prefix(https://example.com/index)
      

You can specify the rules on a domain level and for all the pages related to it:

examples

example: @document - domain level

        @document domain(example.com)
      

You can also specify the rules for a predefined regex pattern you defined for more control:

examples

example: @document - url

        @document regexp("https:.*")
      

@font-face was introduced as one of the pathways toward using custom fonts on the web, bringing more stylish typographies to web pages.

@font-face is a nested rule, and with it, you get different properties for defining the font:

ascent-override : defines the ascent metric for the font. The ascent metric is the height above the baseline that CSS uses to lay out line boxes in an inline formatting context.

Syntax: {ascent-override: normal | percentage;}

descent-override : defines the descent metric for the font. The descent metric is the height below the baseline that CSS uses to lay out line boxes in an inline formatting context.

Syntax: {descent-override: normal | percentage;}

font-display : determines how a font face is displayed based on whether and when it is downloaded and ready to use.

Syntax: {font-display: auto | block | swap | fallback | optional;}

font-family : specifies a name that will be used as the font face value for font properties. You get access to an identifier name for your custom font when and if it is downloaded and available to be used.

Syntax: {font-family: "family-name";}

Example: @font-face - font-family

        @font-face {
          font-family: "CustomFont";
        }
        // Possible usage
        p {
          font-family: 'CustomFont';
        }
      

font-stretch : a font-stretch value. Accepts two values to specify a range that is supported by a font-face, for example "font-stretch: 50% 200%;"

Syntax: {font-stretch: ultra-condensed | extra-condensed | condensed | semi-condensed | normal | semi-expanded | expanded | extra-expanded | ultra-expanded | percentage ;}

font-style : a font-style value. Accepts two values to specify a range that is supported by a font-face, for example "font-style: oblique 20deg 50deg;"

Syntax: {font-style: normal | italic | oblique | oblique with angle (30deg) | oblique with angle range (30deg 50deg);}

font-weight : a font-weight value. Accepts two values to specify a range that is supported by a font-face, for example "font-weight: 100 400;"

Syntax: {font-weight: normal | bold | number | multiple values;}

font-feature-settings : allows control over advanced typographic features in OpenType fonts.

font-variation-settings : allows low-level control over OpenType or TrueType font variations, by specifying the four letter axis names of the features to vary, along with their variation values.

Syntax: {font-variation-settings: normal | <string> <number>;}

line-gap-override : defines the line gap metric for the font.

Syntax: {line-gap-override: normal | percentage;}

size-adjust : defines a multiplier for glyph outlines and metrics associated with this font. This makes it easier to harmonize the designs of various fonts when rendered at the same font size.

Syntax: {size-adjust: percentage;}

src : specifies references to font resources including hints about the font format and technology. It is required for the @font-face rule to be valid. You define the source of the font data. The font data can come from an external source using url() or local one using local(). This way, if the font is not available locally in the site directory or user system, it will be downloaded from the external source. Additionally, you can pass a format parameter, to hint toward the format of the defined font.

Syntax: {src: url() | format() | tech() | local(<font-face-name>) | <font-face-name>;}

Example: @font-face - src

        @font-face {
          font-family: 'Helvetica';
          src:  url('Helvetica') format('woff'),
                local('Helvetica.woff') format('woff');
        }
      

unicode-range : the range of Unicode code points to be used from the font.

@keyframes is a very handy rule for defining CSS animation rules.

With the CSS rules applied within @keyframes rule, we define CSS rules that need to be applied when the CSS animation name attached to the @keyframe rule is applied to an element.

Syntax:

      @keyframes slidein {
        from {transform: translateX(0%);}
        to {transform: translateX(100%);}
      }
    

Values:

custom-ident : a name identifying the keyframe list. This must match the identifier production in CSS syntax.

from : a starting offset of 0%.

to : an ending offset of 100%.

percentage : a percentage of the time through the animation sequence at which the specified keyframe should occur.

Example: @keyframes

        // CSS
        @keyframes ANIMATION-NAME {
          0%   { opacity: 0; }
          100% { opacity: 1; }
        }
        // OR
        @keyframes ANIMATION-NAME {
          from { opacity: 0; }
          to { opacity: 1; }
        }
      

@media is one of the most common CSS at-rules, used for setting CSS styles that will be applied to elements at different screens and window sizes.

This is mainly used for responsive design, so developers can style elements at different window sizes that tend to resemble common mobile, tablet, and desktop devices.

Syntax:

        /* At the top level of your code */
        @media screen and (min-width: 900px) {
          article {padding: 1rem 3rem;}
        }
        
        /* Nested within another conditional at-rule */
        @supports (display: flex) {
          @media screen and (min-width: 900px) {
            article {display: flex;}
          }
        }
      

Example: @media

        #box {background-color: green;}
        @media only screen and (max-width: 600px) {
          #box {background-color: yellow;}
        }
      

@supports is a conditional group rule that will apply its content if the browser meets the criteria of the given condition.

This rule tests whether a browser supports a feature, then applies the styles for those elements if the condition is met.

The @supports "CSS at-rule" let you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a "feature query" and the rule may be placed at the top level of your code or nested inside any other "conditional group at-rule".
The supports condition consists of one or more "name-value pairs" combined by conjunctions (and), disjunctions (or), and/or negations (not). Precedence of operators can be defined with parentheses.

Syntax:

        @supports (<supports-condition>) {
          /* If the condition is true, use the CSS in this block. */
        }
        //combined conditions
        @supports (<<supports-condition>) and (<supports-condition>) {
          /* If both conditions are true, use the CSS in this block. */
        }

      

Examples

          @supports (display: grid) {
              .site-content {display: grid;}
          }
        
            @supports not (display: flex) {
                .el {display: table;  /* ... */}
            }
                                
        
          @supports (display: grid) {
            .photo-layout {display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));  grid-gap: 2rem;}
        }
      
        /* Check one supported condition */
        @supports (display: flex) {
          .module { display: flex; }
        }
        
        /* Check multiple conditions */
        @supports (display: flex) and (-webkit-appearance: checkbox) {
          .module { display: flex; }
        }
      

@page describes the aspect of layout changes that will be applied when printing the document.

It specifically contains pseudo-elements for styling the ":first page" as well as the ":left" and ":right" margins of the page.

Syntax:

        /* Targets all the pages */
        @page { size: 8.5in 9in; margin-top: 4in; }

        /* Targets all even-numbered pages */
        @page :left { margin-top: 4in; }

        /* Targets all odd-numbered pages */
        @page :right {size: 11in;  margin-top: 4in; }

        /* Targets all selectors with `page: wide;` set */
        @page wide { size: a4 landscape; }
      

Example: @page

 
        @page :first { margin: 1in;}
      

@counter-style defines specific counter styles that are not part of the predefined set of styles.

A @counter-style rule defines how to convert a counter value into a string representation (at candidate recommendation stage).

The initial version of CSS defined a set of useful counter styles (see: list-style-type property). However, although more styles were added to this set of predefined styles over the years, this system proved too restrictive to fulfill the needs of worldwide typography. The @counter-style at-rule addresses this shortcoming in an open-ended manner, by allowing authors to define their own counter styles when the pre-defined styles aren't fitting their needs.

Example: @counter-style

        @counter-style thumbs {
          system: cyclic;
          symbols: "\1F44D";
          suffix: " ";
        }
        
        ul {
          list-style: thumbs;
        }
      

The @font-feature-values CSS at-rule let you use a common name in the font-variant-alternates property for features activated differently in OpenType.

The @font-feature-values at-rule may be used either at the top level of your CSS or inside any CSS conditional-group at-rule.

The @property CSS at-rule is part of the CSS Houdini umbrella of APIs.

It allows developers to explicitly define their CSS custom properties, allowing for property type checking, setting default values, and define whether a property can inherit values or not.

The @layer CSS at-rule is used to declare a cascade layer and can be used to define the order of precedence in case of multiple cascade layers.


feature queries - @supports

top

Feature queries are created using the CSS at-rule @supports, and are useful as they give web developers a way to test to see if a browser has support for a certain feature, and then provide CSS that will only run based on the result of that test.

CSS feature queries are part of the "CSS Conditional Rules module", which also contains the media query @media rule.

Feature queries behave in a similar way to media queries. The difference is that with a media query you are testing something about the environment, in which the web page is running. With feature queries you are testing browser support for CSS features.

Syntax: a feature query consists of the @supports rule, followed by the property name and value you would like to test for. You may not test for a bare property name such as display; the rule requires a property name and a value.

syntax:
        @supports (property: value) {
            CSS rules to apply
          }
    

The "@supports at-rule" associates a block of statements with a "supports condition".

The supports condition consists of one or more name-value pairs combined by conjunctions (and), disjunctions (or), and/or negations (not).
Precedence of operators can be defined with parentheses.

Declaration syntax: the most basic supports condition is a simple declaration (a property name followed by a value, separated by a colon). The declaration must be surrounded by parentheses.

examples

the following example returns true if the browser's "transform-origin" property considers 5% 5% valid:

              @supports (transform-origin: 5% 5%) {}
          

Function syntax: the second basic supports condition is a supports function, the syntax for these is supported by all browsers, but the functions themselves are still being standardized.

examples

Tests if the browser supports the tested selector syntax. The following example returns true if the browser supports the child combinator:

              @supports selector(A > B) {}
          

The "@supports CSS at-rule" lets you specify declarations that depend on a browser's support for one or more specific CSS features. This is called a feature query. The rule may be placed at the top level of your code or nested inside any other "conditional group at-rule".

examples

Example : @supports

          @supports (display: grid) {
              div {
                display: grid;
              }
          }
          

Example: @supports not

          @supports not (display: grid) {
              div {
                float: right;
              }
          }
      

In JavaScript, @supports can be accessed via the CSS object model interface "CSSSupportsRule".

The "not" operator can precede any expression to create a new expression, resulting in the negation of the original one.

examples

The following example returns true if the browser's transform-origin property doesn't consider 10em 10em 10em valid:

              @supports not (transform-origin: 10em 10em 10em) {}
          

As with any operator, the "not" operator can be applied to a declaration of any complexity.

examples

the following examples are both valid:

              @supports not (not (transform-origin: 2px)) {}
              @supports (display: grid) and (not (display: inline-grid)) {}
          

The "and" operator creates a new expression from the conjunction of two shorter expressions. It returns true only if both of the shorter expressions are also true.

examples

the following example returns true if and only if the two shorter expressions are simultaneously true:

              @supports (display: table-cell) and (display: list-item) {}
          

Multiple conjunctions can be juxtaposed without the need of more parentheses.

examples

the following are both equivalent:

              @supports (display: table-cell) and (display: list-item) and (display:contents) {}
              @supports (display: table-cell) and ((display: list-item) and (display:contents)) {}
          

The "or" operator creates a new expression from the disjunction of two shorter expressions. It returns true if one or both of the shorter expressions is also true.

examples

the following example returns true if at least one of the two shorter expressions is true:

              @supports (transform-style: preserve) or (-moz-transform-style: preserve) {}
          

Multiple disjunctions can be juxtaposed without the need of more parentheses.

examples

the following are both equivalent:

              @supports (transform-style: preserve) or (-moz-transform-style: preserve) or (-o-transform-style: preserve) or (-webkit-transform-style: preserve) {}
              @supports (transform-style: preserve-3d) or ((-moz-transform-style: preserve-3d) or ((-o-transform-style: preserve-3d) or (-webkit-transform-style: preserve-3d))) {}
          

Note: When using both "and" and "or" operators, the parentheses must be used to define the order in which they apply. Otherwise, the condition is invalid and the whole rule is ignored.


@property

top

@property let developers define custom properties with specific characteristics, allowing for property type checking and constraining, setting default values, and defining whether a custom property can inherit values or not.

The @property rule represents a custom property registration directly in a stylesheet without having to run any JavaScript. Valid @property rules result in a registered custom property, which is similar to calling "registerProperty()" with equivalent parameters.

Syntax:

@property = @property <custom-property-name> { }

CSS:


      @property --rotation {
        syntax: "";
        inherits: false;
        initial-value: 45deg;
      }
      
      @property --myColor {
        syntax: "";
        inherits: true;
        initial-value: lightgray;
      }
    

property name : a <dashed-ident> that starts with -- and is followed by a valid, user-defined identifier. It is case-sensitive.

syntax: a string that describes the allowed value types for the registered custom property. May be a data type name (such as <color>, <length>, or <number>, etc.), with multipliers (+, #) and combinators (|), or a custom ident.

inherits : a boolean value that controls whether the custom property registration specified by @property inherits by default.

initial-value : a value that sets the starting value for the property.

The following conditions must be met for the @property rule to be valid:

The @property rule must include both the syntax and inherits descriptors. If either is missing, the entire @property rule is invalid and ignored.

The initial-value descriptor is optional if the value of the syntax descriptor is the universal syntax definition (that is, syntax: "*"). If the initial-value descriptor is required but omitted, the entire @property rule is invalid and ignored.

Unknown descriptors are invalid and ignored, but do not invalidate the @property rule.

Examples

Item one
Item two
Item three
code:
          <div class="container">
            <div class="item one">Item one</div>
            <div class="item two">Item two</div>
            <div class="item three">Item three</div>
          </div>
          <style>
            @ property --item-size {
              syntax: "<percentage>";
              inherits: true;
              initial-value: 40%;
            }
            .container {display: flex; height: 200px; border: 1px dashed black; /* set custom property values on parent */ 
                      --item-size: 20%;
              --item-color: orange;}
            /* use custom properties to set item size and background color */
            .item {width: var(--item-size); height: var(--item-size);background-color: var(--item-color); }
            /* set custom property values on element itself */
              .two {--item-size: initial; --item-color: inherit;}
              .three {/* invalid values */ --item-size: 1000px; --item-color: xyz;}
          </style>
          <script>
            window.CSS.registerProperty({
              name: "--item-color",
              syntax: "<color>",
              inherits: false,
              initialValue: "aqua",
            });
          </script>
        


@scope

top

Scoping in CSS is in the form of an @scope at-rule that declares a block of CSS to only apply to the given selector. And optionally, stop applying at another given selector.

The CSS @scope rule allows to select elements in specific DOM subtrees.With this at-rule elements can be precisely without writing overly-specific selectors.

This at-rule also reduces coupling between selectors and the DOM structure.

Look at the following HTML:

      <div class="container">
        <div class="news">
          <h2>Some header</h2>
          <img src="example.jpg" alt="Some image">
        </div>
      </div>
    

Here we have some nested <div> elements, and if we want to style the <h2> and <img> elements within the container/news section above you must write (without using @scope):

          <style>
            .container .news h2 {font-size: 30px;color: green;}
            .container .news img { border: 5px solid maroon;}
          </style>
        

With the @scope rule you can target elements precisely without writing overly-specific selectors, like this:

            <style>
              @scope (.container) {
                h2 {font-size: 30px; color: green;}
                img {border: 5px solid maroon;}
              }
            </style>
          

The @scope rule contains one or more rulesets, and can be used in two ways:

As a standalone block inside CSS, in which case it includes a prelude section that includes scope root and optional scope limit selectors - these define the upper and lower bounds of the scope.

As inline styles included inside a <style> element in HTML, in which case the prelude is omitted, and the enclosed ruleset is automatically scoped to the <style> element's enclosing parent element.

example

In scope.

Out of scope.

In scope.

Out of scope.

Out of scope.

In scope.

code:
          <div class="component">
          <p>In scope.</p>
          <div class="content">
            <p>Out of scope.</p>
          </div>
          <div>
            <p>In scope.</p>
          </div>
          <div class="slot">
            <p>Out of scope.</p>
          </div>
          <div class="child-component">
            <p>Out of scope.</p>
          </div>
          <div>
            <p>In scope.</p>
          </div>
        </div>
        <style>
          @scope (.component) to (.content, .slot, .child-component) {
            p {color: red;}
          }
        .component {border: solid 2px; width: fit-content; padding: 16px 40px; font-family: system-ui;margin-left: 2vw;}
        </style>
      

syntax examples

      <div id="my-component">
        <p>This paragraph is inside the scope.</p>
      </div>
      <p>This paragraph is outside the scope.</p>
      <style>
        @scope (#my-component) {
          p { color: blue;}
        }
      </style>
        
      

practical examples

top

example a

If your browser supports row-gap, the text and border will be red.
code:
              <div>
                  <div class="box" style="margin-left:5vw;">If your  browser supports row-gap, the text and border will be red.</div>
              </div>
              <style>
                  .box {border: 0.5vw solid blue; color: blue;}
                  @supports (row-gap: 1vw) {
                      .box {border: 0.5vw solid red;  color: red;}
                  }
              </style>
          

example b

If your browser does not support row-gap, the text and border will be red.
code:
                  <div>
                      <div class="box-b" style="margin-left:5vw;">If your browser does not support row-gap, the text and border 
                      will be red.</div>
                  </div>
                  <style>
                      .box-b {border: 0.5vw solid blue; color: blue;}
                      @supports not(row-gap: 1vw) {
                          .box-a {border: 0.5vw solid red;  color: red;}
                      }
                  </style>
              

example c

If your browser supports display: grid and shape-outside: circle(), the text and border will be red.
code:
                  <div>
                      <div class="box-c" style="margin-left:5vw;">If your browser supports display: grid and shape-outside: circle(), 
                      the text and border will be red.
                          </div>
                  </div>
                  <style>
                      .box-c {border: 0.5vw solid blue; color: blue;}
                      @supports (display: grid)  and (shape-outside:circle()){
                          .box-c {border: 0.5vw solid red;  color: red;}
                      }
                  </style>
              

example d

Box 1
Has more content
than the other boxes.
Box 2
Box 3
code:
                  <div class="wrapper">
                      <div class="box-1">Box 1<br>Has more content <br>than the other boxes.</div>
                      <div class="box-2">Box 2</div>
                      <div class="box-3">Box 3</div>
                  </div>
                  <style>
                      .box-1 {margin-left:5vw; float: left; width: 30%; border: .2vw solid rgb(95, 97, 110); 
                          border-radius: .5vw; padding: 1vw;}
                      @supports (display: grid) {
                          .wrapper {display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 1vw;}
                          .box-1 {width: auto;}
                      }
                  </style>